package main

import (
	"fmt"
	"reflect"
	"time"
)

/*
channel 是goroutine间的通讯通道。
https://blog.csdn.net/weixin_40310404/article/details/122947958
*/
func main() {
	d2_1()
	fmt.Println("不带缓存的channel")
	d2_2()
	fmt.Println("带缓存的channel")
	d2_3()
	fmt.Println("串行运行")
	d2_4()
	fmt.Println("协程间通讯")
	d2_5()
}

// 创建channel
func d2_1() {
	c1 := make(chan int)    //创建一个可以传输int类型数据的channel，不带缓存，线程阻塞的
	c2 := make(chan int, 3) //创建一个可以传输int类型数据的channel，带缓冲，线程非阻塞。内部持有一个元素队列， 第二个参数就是队列最大容量

	fmt.Println("c1", c1, reflect.TypeOf(c1))
	fmt.Println("c2", c2, reflect.TypeOf(c2))
}

// 1、基于不带缓存的channel。chan 是阻塞的，consumer必须返回producer才能继续
func d2_2() {
	c := make(chan int)

	go d2_2producer(c) // 启动生产者goroutine
	go d2_2consumer(c) // 启动消费者goroutine

	// 等待goroutine执行完成,不等待的话main goroutine退出，子goroutine也会强制退出
	//var input string
	//fmt.Scanln(&input)
}

func d2_2producer(c chan<- int) {
	// 即使关闭,也可以继续使用
	defer close(c)
	for i := 0; i < 5; i++ {
		fmt.Println("producer data:", i)
		c <- i //向channel写入数据
	}
}

func d2_2consumer(c <-chan int) {
	for i := range c {
		fmt.Println("consumer data:", i)
		time.Sleep(1 * time.Second)
	}
}

// 2、带缓存的channel。chan 是非阻塞的，producer发送之后就继续下面的操作
func d2_3() {
	c := make(chan int, 10)

	go d2_3producer(c) // 启动生产者goroutine
	go d2_3consumer(c) // 启动消费者goroutine

	// 等待goroutine执行完成,不等待的话main goroutine退出，子goroutine也会强制退出
	//var input string
	//fmt.Scanln(&input)
}

func d2_3producer(c chan<- int) {
	defer close(c)
	for i := 0; i < 5; i++ {
		fmt.Println("producer data:", i)
		c <- i //向channel写入数据
	}
}

func d2_3consumer(c <-chan int) {
	for i := range c {
		fmt.Println("consumer data:", i)
		time.Sleep(1 * time.Second)
	}
}

func d2_4() {
	naturals := make(chan int)
	squares := make(chan int)
	go counter(naturals)
	go squarer(squares, naturals)
	printer(squares)
}

func counter(out chan<- int) {
	fmt.Println("1.begin")
	for x := 0; x < 100; x++ {
		out <- x
	}
	close(out)
	time.Sleep(1 * time.Second)
	fmt.Println("1.done")
}

func squarer(out chan<- int, in <-chan int) {
	fmt.Println("2.begin")
	for v := range in {
		out <- v * v
	}
	close(out)
	time.Sleep(1 * time.Second)
	fmt.Println("2.done")
}

func printer(in <-chan int) {
	fmt.Println("3.begin")
	sum := 0
	for v := range in {
		sum += v
	}
	time.Sleep(1 * time.Second)
	fmt.Println("3.done, sum=", sum)
}

func d2_5() {
	c := make(chan int)
	// 协程写入数据
	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}
		close(c)
	}()

	// 主线程读取数据
	for i := range c {
		fmt.Println("main:", i)
	}
}
